package partlow.googleplaces;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import parsing.exception.BetterJSONException;
import partlow.googleplaces.datatypes.Place;
import partlow.googleplaces.parsers.PlaceParser;

public class GooglePlacesAPI {
	private static final String DOMAIN = "https://maps.googleapis.com/maps/api/place";
	private static final String SEARCH_ENDPOINT = "/search";
	private static final String DETAILS_ENDPOINT = "/details";
	private static final String JSON_OUTPUT = "/json";
	private static final String API_KEY = "AIzaSyCg39lQFqILL1HbSvT4dXfP-LRlODBiFZU";
	
	public List<Place> getPlaces(Map<String, String> query) throws GooglePlacesException, IOException, JSONException {
		List<Place> places = new ArrayList<Place>();
		StringBuilder searchRequestStringBuilder = new StringBuilder(DOMAIN).append(SEARCH_ENDPOINT)
				 .append(JSON_OUTPUT).append("?key=").append(API_KEY);
		StringBuilder detailsRequestStringBuilder = new StringBuilder(DOMAIN).append(DETAILS_ENDPOINT)
				 .append(JSON_OUTPUT).append("?key=").append(API_KEY);
		
		for(Map.Entry<String, String> entry : query.entrySet()) {
        	String parameterName = entry.getKey();
        	String parameter = entry.getValue();
        	try {
        		parameterName = URLEncoder.encode(parameterName, "UTF-8");
        		parameter = URLEncoder.encode(parameter, "UTF-8");
        	} catch(UnsupportedEncodingException uee) {
        		continue;
        	}
        	
        	searchRequestStringBuilder.append("&").append(parameterName).append("=").append(parameter);
        	detailsRequestStringBuilder.append("&").append(parameterName).append("=").append(parameter);
        }
		String originalSearchURL = searchRequestStringBuilder.toString();
		String originalDetailsURL = detailsRequestStringBuilder.toString();
		
		// query until no more pages
    	List<String> references = new ArrayList<String>();
        while(searchRequestStringBuilder != null) {
        	
        	// get json
        	URL url = new URL(searchRequestStringBuilder.toString());
		    URLConnection urlConnection = url.openConnection();
		    JSONObject jsonObject = buildJSONFromConnection(urlConnection);
		    searchRequestStringBuilder = null;
		    
		    // get status
		    String status;
		    if(jsonObject.has("status"))
		    	status = jsonObject.getString("status");
		    else 
		    	break;
	    	if(!status.equalsIgnoreCase("ok"))
	        	break;
	    	
	    	// get all results references
	    	JSONArray results = jsonObject.getJSONArray("results");
	    	for(int resultsIndex = 0; resultsIndex < results.length(); resultsIndex++) {
	    		JSONObject result = results.getJSONObject(resultsIndex);
	    		references.add(result.getString("reference"));
	    	}
		    
	    	// get page token
		    String pageToken;
		    if(jsonObject.has("next_page_token"))
		    	pageToken = jsonObject.getString("next_page_token");
		    else 
		    	break;
	    	
	    	searchRequestStringBuilder = new StringBuilder(originalSearchURL).append("&pagetoken=").append(pageToken);
		}
        
        // loop through references
        JSONArray results = new JSONArray();
        for(String reference : references) {
        	
        	// get place details
        	detailsRequestStringBuilder = new StringBuilder(originalDetailsURL).append("&reference=").append(reference);
        	URL url = new URL(detailsRequestStringBuilder.toString());
		    URLConnection urlConnection = url.openConnection();
		    JSONObject jsonObject = buildJSONFromConnection(urlConnection);
		    
		    // get status
		    String status;
		    if(jsonObject.has("status"))
		    	status = jsonObject.getString("status");
		    else 
		    	continue;
	    	if(!status.equalsIgnoreCase("ok"))
	    		continue;
	    	
	    	// get results
	    	JSONObject result;
	    	if(jsonObject.has("result"))
	    		result = jsonObject.getJSONObject("result");
	    	else
	    		continue;
	    	
	    	results.put(result);
        }
		
        places.addAll(new PlaceParser().parse(results));
		return places;
	}
	
	/**
     * TODO: There must be a better way than this.  christ.
     * @param connection
     * @return
     * @throws IOException
     * @throws BetterJSONException 
     * @throws JSONException 
     */
    private JSONObject buildJSONFromConnection(URLConnection connection) throws IOException, BetterJSONException{
        String line;
        StringBuilder builder = new StringBuilder();
        BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
        while((line = reader.readLine()) != null){
            builder.append(line);
        }

        String jsonString = builder.toString();
        JSONObject json;
		try {
			json = new JSONObject(jsonString);
		} catch (JSONException e) {
			throw new BetterJSONException(e.getMessage(), jsonString);
		}
        return json;
    }
}
